-- SPDX-License-Identifier: GPL-3.0-or-later

local this_event = {}

local U = require "packages/utility/utility"

this_event.DiscussionPindian = "GameEvent.DiscussionPindian"

Fk:addGameEvent(this_event.DiscussionPindian, nil,
  function (self)
    local discussionData = self.data
    local room = self.room
    local logic = room.logic
    local from = discussionData.from
    discussionData.results = {}

    for _, to in pairs(discussionData.tos) do
      discussionData.results[to] = discussionData.results[to] or {}
    end
    discussionData.color = "noresult"

    logic:trigger(U.StartDiscussion, from, discussionData)
    logic:trigger(fk.StartPindian, from, discussionData)

    if discussionData.reason ~= "" then
      room:sendLog{
        type = "#StartDiscussionReason",
        from = from.id,
        arg = discussionData.reason,
      }
    end

    -- 将拼点牌变为虚拟牌
    ---@param card Card
    local virtualize = function (card)
      local _card = card:clone(card.suit, card.number)
      if card:getEffectiveId() then
        _card.subcards = { card:getEffectiveId() }
      end
      return _card
    end

    local targets = {} ---@type ServerPlayer[]
    local moveInfos = {}
    local results = discussionData.results
    for _, to in ipairs(discussionData.tos) do
      if results[to] and results[to].toCard then
        results[to].toCard = virtualize(results[to].toCard)

        local cid = results[to].toCard:getEffectiveId()
        if cid and room:getCardArea(cid) ~= Card.Processing and
          not table.find(moveInfos, function (info)
            return table.contains(info.ids, cid)
          end) then
          table.insert(moveInfos, {
            ids = { cid },
            from = room.owner_map[cid],
            toArea = Card.Processing,
            moveReason = fk.ReasonPut,
            skillName = discussionData.reason,
            moveVisible = true,
          })
        end
      elseif not to:isKongcheng() then
        table.insert(targets, to)
      end
    end

    if #targets > 0 then
      local req = Request:new(targets, "AskForUseActiveSkill")
      req.focus_text = discussionData.reason

      local extraData = {
        num = 1,
        min_num = 1,
        include_equip = false,
        pattern = ".",
        reason = discussionData.reason,
      }

      local data = { "choose_cards_skill", "#askForDiscussion", false, extraData }
      for _, p in ipairs(targets) do
        req:setData(p, data)
        req:setDefaultReply(p, table.random(p:getCardIds("h"), 1))
      end
      req:ask()

      for _, p in ipairs(targets) do
        discussionData.results[p] = discussionData.results[p] or {}
        local result = req:getResult(p)

        if result ~= "" then
          local id
          if result.card then
            id = result.card.subcards[1]
          else
            id = result[1]
          end
          discussionData.results[p].toCards = {id}
          discussionData.results[p].opinion = Fk:getCardById(id):getColorString()

          local card = virtualize(Fk:getCardById(id))

          if p == from then
            discussionData.fromCard = card
          else
            discussionData.results[p].toCard = card
          end

          if not table.find(moveInfos, function (info)
            return table.contains(info.ids, card:getEffectiveId())
          end) then
            table.insert(moveInfos, {
              ids = { card:getEffectiveId() },
              from = p,
              toArea = Card.Processing,
              moveReason = fk.ReasonPut,
              skillName = discussionData.reason,
              moveVisible = true,
            })
          end
        end
      end
    end

    if #moveInfos > 0 then
      room:moveCards(table.unpack(moveInfos))
    end
    logic:trigger(fk.PindianCardsDisplayed, nil, discussionData)

    for _, p in ipairs(discussionData.tos) do
      discussionData.results[p] = discussionData.results[p] or {}
      local result = discussionData.results[p]
      local fromCard, toCard = discussionData.fromCard, result.toCard

      if discussionData.results[p].toCards then
        p:showCards(discussionData.results[p].toCards)
      end

      if discussionData.results[p].opinion then
        room:sendLog{
          type = "#SendDiscussionOpinion",
          from = p.id,
          arg = discussionData.results[p].opinion,
          toast = true,
        }
      end

      if fromCard and toCard then
        if fromCard.number > toCard.number then
          result.winner = from
        elseif fromCard.number < toCard.number then
          result.winner = p
        end

        local singlePindianData = {
          from = from,
          to = p,
          fromCard = fromCard,
          toCard = toCard,
          winner = result.winner,
          reason = discussionData.reason,
        }

        logic:trigger(fk.PindianResultConfirmed, nil, singlePindianData)
      end
    end
    logic:trigger(U.DiscussionCardsDisplayed, nil, discussionData)

    discussionData.opinions = {}
    for to, result in pairs(discussionData.results) do
      if result.toCards then
        for _, id in ipairs(result.toCards) do
          local color = Fk:getCardById(id):getColorString()
          discussionData.opinions[color] = (discussionData.opinions[color] or 0) + 1
        end
      else
        discussionData.opinions[result.opinion] = (discussionData.opinions[result.opinion] or 0) + 1
      end
    end
    logic:trigger(U.DiscussionResultConfirming, from, discussionData)

    local max, result = 0, {}
    for color, count in pairs(discussionData.opinions) do
      if color ~= "nocolor" and color ~= "noresult" and count > max then
        max = count
      end
    end
    for color, count in pairs(discussionData.opinions) do
      if color ~= "nocolor" and color ~= "noresult" and count == max then
        table.insert(result, color)
      end
    end
    if #result == 1 then
      discussionData.color = result[1]
    end

    room:sendLog{
      type = "#ShowDiscussionResult",
      from = from.id,
      arg = discussionData.color,
      toast = true,
    }

    logic:trigger(U.DiscussionResultConfirmed, from, discussionData)

    if logic:trigger(U.DiscussionFinished, from, discussionData) then
      logic:breakEvent()
    end
    if logic:trigger(fk.PindianFinished, from, discussionData) then
      logic:breakEvent()
    end
    if not this_event.interrupted then return end
  end,
  function (self)
    local data = self.data
    local room = self.room

    local toThrow = {}
    local cid = data.fromCard and data.fromCard:getEffectiveId()
    if cid and room:getCardArea(cid) == Card.Processing then
      table.insertIfNeed(toThrow, cid)
    end

    for _, result in pairs(data.results) do
      cid = result.toCard and result.toCard:getEffectiveId()
      if cid and room:getCardArea(cid) == Card.Processing then
        table.insertIfNeed(toThrow, cid)
      end
    end

    if #toThrow > 0 then
      room:moveCards({
        ids = toThrow,
        toArea = Card.DiscardPile,
        moveReason = fk.ReasonPutIntoDiscardPile,
      })
    end
    if not self.interrupted then return end
  end
)

---@param player ServerPlayer @ 发起议事的角色
---@param tos ServerPlayer[] @ 参与议事的角色
---@param reason string? @ 议事技能名
---@param extra_data any? @ extra_data
this_event.discussion = function (player, tos, reason, extra_data)
  local discussionData = U.DiscussionData:new{
    from = player,
    tos = tos,
    reason = reason or "AskForDiscussion",
    extra_data = extra_data or {},
  }
  local event = GameEvent[this_event.DiscussionPindian]:create(discussionData)
  local _, ret = event:exec()
  return discussionData
end

return this_event